package uk.co.cdal.cobweb.frontend.GUI;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.event.MouseInputListener;
import javax.swing.text.DefaultHighlighter;

import uk.co.cdal.cobweb.backend.BackendImpl;
import uk.co.cdal.cobweb.frontend.Frontend;
import uk.co.cdal.cobweb.shared.Idea;

public class SpiderNode extends JPanel implements VisualNode {
	
	private static final long serialVersionUID = 1L;
	private String title;
	private JTextField titleField;
	private boolean titleEnabled;
	
	private String text;
	private JTextArea textarea;
	private boolean textareaEnabled;
	
	private Color backgroundColor;
	private Color foregroundColor;
	private Color mouseoverColor;
	private Color borderColor;
	private Color edgeColor;
	
	private String id;
	
	private ArrayList<String> url;
	
	private ArrayList<ImageIcon> images;
	
	private Idea idea;
	
	private HashMap<String,Edge> edges;
	
	private DrawingPanel dp;
	
	private SpiderNode that;
	
	
	public SpiderNode(int x, int y, Idea i) {
		// Set the drawing panel reference
		dp = Frontend.getFrontend().getDrawingPanel();
		
		// Set that for access from actionlisteners
		that = this;
		
		// Give the node a default title and text
		title = "Place text here";
		text = "Greater detail about your idea";
		
		// Get a unique ID for the node
		id = i.getId();
		
		// Copy reference of the node
		setIdea(i);
		
		// Initilise information arrays
		url = new ArrayList<String>();
		images = new ArrayList<ImageIcon>();
		edges = new HashMap<String, Edge>();
		
		// Initialise cordinates
		this.setLocation(x, y);
		
		// Set the layout manager + contraints object.
		setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		
		// Set up colors these will only be a color if customised by user.
		backgroundColor = null;
		foregroundColor = null;
		mouseoverColor = null;
		borderColor = null;
		edgeColor = null;
		
		// Set the background
		setBackground(getBackgroundColor());
		
		//Draw the borders
		setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createLineBorder(getBorderColor(), 2),
				BorderFactory.createEmptyBorder(5, 5, 5, 5)));
		
		// Add the title text field
		titleField = new JTextField(title);
		// Add action listener
		titleField.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (e.getButton() == MouseEvent.BUTTON1 
						&& e.getClickCount() == 2) {
					setTitleEditable(!titleEnabled);
				}
			}
		});
		titleField.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				setTitleEditable(false);				
			}
		});
		c.anchor = GridBagConstraints.LINE_START;
		c.fill = GridBagConstraints.VERTICAL;
		c.ipadx = 0;
		c.ipady = 0;
		c.gridwidth = 1;
		c.gridx = 0;
		c.gridy = 0;
		c.weightx = 1.0;
		add(titleField, c);
		setTitleEditable(false);
		Insets emptyMargin = new Insets(0,0,0,0);
		
		// Add the create edge button top right
		JButton createEdge = new JButton(new ImageIcon("icons/arrow_up.png"));
		createEdge.setToolTipText("Click here to connect this idea to another");
		createEdge.setOpaque(false);
		createEdge.setIconTextGap(0);
		createEdge.setMargin(emptyMargin);
		createEdge.setContentAreaFilled(false);
		createEdge.setBorderPainted(false);
		createEdge.setFocusPainted(false);
		//TODO: Add action listener
		createEdge.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (e.getButton() == MouseEvent.BUTTON1) {
					dp.setInLink(true);
					dp.setNodeInLink(that);
				}
			}
		});
		c.anchor = GridBagConstraints.LINE_END;
		c.fill = GridBagConstraints.NONE;
		c.ipadx = 2;
		c.ipady = 2;
		c.gridwidth = 1;
		c.gridx = 1;
		c.gridy = 0;
		c.weightx = 0.0;
		add(createEdge, c);
		
		//Add the delete node button top right (HCI compliant)
		JButton remove = new JButton(new ImageIcon("icons/cancel.png"));
		remove.setToolTipText("Click here to delete this idea");
		remove.setOpaque(false);
		remove.setIconTextGap(0);
		remove.setMargin(emptyMargin);
		remove.setContentAreaFilled(false);
		remove.setBorderPainted(false);
		remove.setFocusPainted(false);
		remove.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (e.getButton() == MouseEvent.BUTTON1) {
					Frontend.getFrontend().getDrawingPanel().removeIdea(getIdea());
				}
			}
		});
		c.anchor = GridBagConstraints.LINE_END;
		c.fill = GridBagConstraints.NONE;
		c.ipadx = 2;
		c.ipady = 2;
		c.gridwidth = 1;
		c.gridx = 2;
		c.gridy = 0;
		c.weightx = 0.0;
		add(remove, c);
		
		JSeparator sep = new JSeparator();
		sep.setForeground(borderColor);
		
		// Add the text area using in 1,0, colspan = 3
		textarea = new JTextArea(text);
		setTextAreaEditable(false);
		
		// Add action listener
		textarea.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (e.getButton() == MouseEvent.BUTTON1 
						&& e.getClickCount() == 2) {
					setTextAreaEditable(!textareaEnabled);
				}
			}
		});
		titleField.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				setTextAreaEditable(false);				
			}
		});
		
		c.ipadx = 2;
		c.ipady = 2;
		c.gridwidth = 3;
		c.insets = new Insets(3,0,3,0);
		c.fill = GridBagConstraints.BOTH;
		c.gridx = 0;
		c.gridy = 1;
		c.weightx = 1.0;		
		add(textarea, c);
		
		// Add the URL label
		// TODO: Add URL Panel here instead of seperate components
		JLabel URLTextBox = new JLabel("URL's go here");
		URLTextBox.setEnabled(false);
		c.ipadx = 2;
		c.ipady = 2;
		c.gridwidth = 2;
		c.gridx = 0;
		c.gridy = 2;
		c.weightx = 1.0;
		add(URLTextBox, c);
		
		// Add the URL button
		JButton URLButton = new JButton(new ImageIcon("icons/link_add.png"));
		URLButton.setToolTipText("Click here to attach a URL");
		URLButton.setOpaque(false);
		URLButton.setIconTextGap(0);
		URLButton.setMargin(new Insets(0,0,0,0));
		URLButton.setContentAreaFilled(false);
		URLButton.setBorderPainted(false);
		URLButton.setFocusPainted(false);
		c.ipadx = 2;
		c.ipady = 2;
		c.gridwidth = 1;
		c.fill = GridBagConstraints.BOTH;
		c.gridx = 2;
		c.gridy = 2;
		c.weightx = 0.0;
		add(URLButton, c);
		
		// Add the image label
		// TODO: Add Image Panel here instead if seperate components.
		JLabel imageLabel = new JLabel("Add images here");
		c.ipadx = 2;
		c.ipady = 2;
		c.gridwidth = 2;
		c.gridx = 0;
		c.gridy = 3;
		c.weightx = 1.0;
		add(imageLabel, c);
		
		// Add the image button
		JButton imageButton = new JButton(new ImageIcon("icons/image_add.png"));
		imageButton.setToolTipText("Click here to attach an image");
		imageButton.setOpaque(false);
		imageButton.setIconTextGap(0);
		imageButton.setMargin(emptyMargin);
		imageButton.setContentAreaFilled(false);
		imageButton.setBorderPainted(false);
		imageButton.setFocusPainted(false);
		c.ipadx = 2;
		c.ipady = 2;
		c.gridwidth = 1;
		c.fill = GridBagConstraints.BOTH;
		c.gridx = 2;
		c.gridy = 3;
		c.weightx = 0.0;
		add(imageButton, c);
		
		// Add action listeners
		SpiderNodeMouseListener mlistener = new SpiderNodeMouseListener();
		KeyListener klistener = new SpiderNodeKeyboardListener();
		addMouseListener(mlistener);
		addKeyListener(klistener);
		addMouseMotionListener(mlistener);
		
		// Allow the node to be seen and enabled.
		setVisible(true);
		setEnabled(true);
		
		// Set the tool tip text of the node
		setToolTipText("Click and drag here to position the node.");		
		
	}
	
	public void setTitleEditable(boolean editable) {
		if (editable) {
			// make editable
			titleField.setHighlighter(new DefaultHighlighter());
			titleField.setEditable(true);
			titleField.setBackground(getBackgroundColor());
			titleField.setForeground(getForegroundColor());
			titleField.setFont(titleField.getFont().deriveFont(Font.BOLD));
			titleField.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
			
			titleEnabled = true;
		}
		else {
			// make uneditable
			titleField.setHighlighter(null);
			titleField.setEditable(false);
			titleField.setBackground(getBackgroundColor());
			titleField.setForeground(getForegroundColor());
			titleField.setFont(titleField.getFont().deriveFont(Font.BOLD));
			titleField.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
			
			// TODO: Save updated title
//			try {
//				Frontend.getFrontend().getBackend().updateTitle(titleField.getText(), getIdea(), Frontend.getFrontend().getBackend().getAddress());
//			}
//			catch(RemoteException e) {
//				BackendImpl.log.severe("Cannot resolve local host");
//			}
			titleEnabled = false;
		}
	}
	
	public void setTextAreaEditable(boolean editable) {
		if (editable) {
			// make editable
			textarea.setHighlighter(new DefaultHighlighter());
			textarea.setEditable(true);
			textarea.setBackground(getBackgroundColor());
			textarea.setForeground(getForegroundColor());
			textarea.setFont(textarea.getFont().deriveFont(Font.BOLD));
			textarea.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
			
			textareaEnabled = true;
		}
		else {
			// make uneditable
			textarea.setHighlighter(null);
			textarea.setEditable(false);
			textarea.setBackground(getBackgroundColor());
			textarea.setForeground(getForegroundColor());
			textarea.setFont(textarea.getFont().deriveFont(Font.BOLD));
			textarea.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
			
			// Update text on all nodes
//			try {
//				Frontend.getFrontend().getBackend().updateFurtherText(textarea.getText(), getIdea(), Frontend.getFrontend().getBackend().getAddress());
//			}
//			catch(RemoteException e) {
//				BackendImpl.log.severe("Cannot resolve localhost");
//			}
			textareaEnabled = false;
		}
	}
	
	
	/**
	 * @return the center of the visual display for this component - X axis
	 */
	public int getCenterX() {
		return getX() + (getWidth() / 2);
	}
	
	/**
	 * 
	 * @return The center of the visual display for this component - Y axis
	 */
	public int getCenterY() {
		return getY() + (getHeight() / 2);
	}
	
	/**
	 * Add an edge to another node
	 * @param nodeID The other end of the edge
	 */
	public void addEdge(SpiderNode child) {
		if(!edges.containsKey(child.getId())) {
			edges.put(child.getId(), new SpiderEdge(this, child));
		}
	}
	
	/**
	 * Remove an edge from another node
	 * @param nodeID The other end of the edge
	 */
	public void removeEdge(SpiderNode child) {
		if(edges.containsKey(child.getId())) {
			edges.remove(child.getId());
		}
	}
	
	public void updateEdges(Graphics g) {
		ArrayList<Edge> outEdges = new ArrayList<Edge>(edges.values());
		if(outEdges.size() > 0) {
			for(int i = 0; i < outEdges.size(); i++) {
				if(outEdges.get(i) != null) {
					outEdges.get(i).draw(g);
				}
			}
		}
		
	}
	
	public void updateEdge(String nodeID, Graphics g) {
		edges.get(nodeID).draw(g);
	}


	/**
	 * @return the id
	 */
	public String getId() {
		return id;
	}


	/**
	 * @param id the id to set
	 */
	public void setId(String id) {
		this.id = id;
	}


	/**
	 * @return the text
	 */
	public String getText() {
		return text;
	}


	/**
	 * @param text the text to set
	 */
	public void setText(String text) {
		this.text = text;
	}


	/**
	 * @return the title
	 */
	public String getTitle() {
		return title;
	}


	/**
	 * @param title the title to set
	 */
	public void setTitle(String title) {
		this.title = title;
	}

	/**
	 * Add a url to the list to be displayed
	 * 
	 * @param url The url to add to the collection of urls
	 */
	public void addURL(String url) {
		this.url.add(url);
	}
	
	/**
	 * Remove URL from the collection of urls
	 * 
	 * @param url The url to remove from the url list
	 */
	public void removeURL(String url) {
		this.url.remove(url);
	}
	
	/**
	 * Add an image to the collection of images
	 * @param i The image to add to the collection of images
	 */
	public void addImage(ImageIcon i) {
		images.add(i);
	}
	
	/**
	 * Remove an image from the collection of images.
	 * @param i The image to remove.
	 */
	public void removeImage(Image i) {
		images.remove(i);
	}

	/**
	 * @return the idea
	 */
	public Idea getIdea() {
		return idea;
	}

	/**
	 * @param idea the idea to set
	 */
	public void setIdea(Idea idea) {
		this.idea = idea;
	}
	
	/**
	 * @return the images
	 */
	public ArrayList<ImageIcon> getImages() {
		return images;
	}

	/**
	 * @param images the images to set
	 */
	public void setImages(ArrayList<ImageIcon> images) {
		this.images = images;
	}

	/**
	 * @return the url
	 */
	public ArrayList<String> getUrl() {
		return url;
	}

	/**
	 * @param url the url to set
	 */
	public void setUrl(ArrayList<String> url) {
		this.url = url;
	}
	
	/**
	 * @return the backgroundColor
	 */
	public Color getBackgroundColor() {
		if(backgroundColor == null) {
			return dp.getBackground();
		}
		return backgroundColor;
	}

	/**
	 * @param backgroundColor the backgroundColor to set
	 */
	public void setBackgroundColor(Color backgroundColor) {
		this.backgroundColor = backgroundColor;
	}

	/**
	 * @return the foregroundColor
	 */
	public Color getForegroundColor() {
		if(foregroundColor == null) {
			return dp.getForeground();
		}
		return foregroundColor;
	}

	/**
	 * @param foregroundColor the foregroundColor to set
	 */
	public void setForegroundColor(Color foregroundColor) {
		this.foregroundColor = foregroundColor;
	}

	/**
	 * @return the mouseoverColor
	 */
	public Color getMouseoverColor() {
		if(mouseoverColor == null) {
			return dp.getMouseOverColor();
		}
		return mouseoverColor;
	}

	/**
	 * @param mouseoverColor the mouseoverColor to set
	 */
	public void setMouseoverColor(Color mouseoverColor) {
		this.mouseoverColor = mouseoverColor;
	}

	/**
	 * @return the borderColor
	 */
	public Color getBorderColor() {
		if(borderColor == null) {
			return dp.getBorderColor();
		}
		return borderColor;
	}

	/**
	 * @param borderColor the borderColor to set
	 */
	public void setBorderColor(Color borderColor) {
		this.borderColor = borderColor;
	}

	/**
	 * @return the edgeColor
	 */
	public Color getEdgeColor() {
		if(edgeColor == null) {
			return dp.getEdgeColor();
		}
		return edgeColor;
	}

	/**
	 * @param edgeColor the edgeColor to set
	 */
	public void setEdgeColor(Color edgeColor) {
		this.edgeColor = edgeColor;
	}
	
	/**
	 * @author seb
	 *
	 */
	private class SpiderNodeMouseListener extends MouseAdapter implements MouseInputListener {
		
		private boolean inDrag = false;		
		private Rectangle outliner;	
		private BufferedImage outlinerImage;
		private Point start;
		private Dimension offset;
		private Point end;
		private Frontend frontend = Frontend.getFrontend();
		private DrawingPanel dp = frontend.getDrawingPanel();
		

		public void mouseEntered(MouseEvent e) {
			if (!dp.isInDrag()) {
				setBackground(new Color(179,190,255));
			}
			
		}

		public void mouseExited(MouseEvent e) {
			if(!dp.isInDrag()) {
				//TODO: Abstract this to fields
				setBackground(getParent().getBackground());
			}
			
		}

		public void mousePressed(MouseEvent e) {
			start = new Point(getX(), getY());
			offset = new Dimension(e.getX(), e.getY());
			outliner = new Rectangle(start.x, start.y, getWidth(), getHeight());
			outlinerImage = new BufferedImage(getWidth(), getHeight(),
					BufferedImage.TYPE_INT_RGB);
			paint(outlinerImage.getGraphics());
			((DrawingPanel)getParent()).setOutliner(outliner, outlinerImage);
			inDrag = true;
			dp.setInDrag(true);
			dp.setNodeInDrag(that);
		}

		public void mouseReleased(MouseEvent e) {			
			outliner = null;
			outlinerImage = null;
			((DrawingPanel)getParent()).setOutliner(outliner, outlinerImage);
			inDrag = false;
			dp.setInDrag(false);
			dp.setNodeInDrag(null);
			if (end != null) setLocation(end);				
			setVisible(true);
			start = null;
			end = null;
			
			dp.setCurSelected(that);
			dp.repaint();
			
			if (!inDrag) {
				DrawingPanel parent = Frontend.getFrontend().getDrawingPanel();
				if(e.getButton() == 3) {
					parent.addIdea(getX(), getY() + getHeight() + 20, getIdea());
					//parent.update();
				}
			}					
		}

		public void mouseDragged(MouseEvent e) {
			if (inDrag) {
				setVisible(false);
				end = new Point(getX() + e.getX() - offset.width, getY() + e.getY() - offset.height);
				outliner = new Rectangle(end.x, end.y, getWidth(), getHeight());
				((DrawingPanel)getParent()).setOutliner(outliner, outlinerImage);
				getParent().repaint();
			}
		}

		public void mouseMoved(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}
	}
		
	private class SpiderNodeKeyboardListener extends KeyAdapter {

		public void keyPressed(KeyEvent e) {
			// TODO Auto-generated method stub
			
		}

		public void keyReleased(KeyEvent e) {
			// TODO Auto-generated method stub
			
		}

		public void keyTyped(KeyEvent e) {
			// TODO Auto-generated method stub
			
		}
	}

	/**
	 * @return the edges
	 */
	public HashMap<String, Edge> getEdges() {
		return edges;
	}

	/**
	 * @param edges the edges to set
	 */
	public void setEdges(HashMap<String, Edge> edges) {
		this.edges = edges;
	}
}
